using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

namespace  EasyNetWork
{
    public class TcpNetworkChannel : NetworkChannelBase
    {
        private readonly AsyncCallback m_ConnectCallback;
        private readonly AsyncCallback m_SendCallback;
        private readonly AsyncCallback m_ReceiveCallback;
        
        /// <summary>
        /// 初始化网络频道的新实例。
        /// </summary>
        /// <param name="name">网络频道名称。</param>
        /// <param name="networkChannelHelper">网络频道辅助器。</param>
        public TcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
            : base(name, networkChannelHelper)
        {
            m_ConnectCallback = ConnectCallback;
            m_SendCallback = SendCallback;
            m_ReceiveCallback = ReceiveCallback;
        }
        
        /// <summary>
        /// 获取网络服务类型。
        /// </summary>
        public override ServiceType ServiceType
        {
            get
            {
                return ServiceType.TCP;
            }
        }
        
        /// <summary>
        /// 连接到远程主机
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="port"></param>
        /// <param name="userdata"></param>
        /// <exception cref="Exception"></exception>
        public override void Connect(IPAddress ipAddress,int port,object userdata)
        {
            base.Connect(ipAddress,port,userdata);
            m_Socket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            if (m_Socket == null)
            {
                string errorMessage = "初始化网络频道失败";
                if (NetworkChannelError != null)
                {
                    NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
                    return;
                }

                throw new System.Exception(errorMessage);
            }
            
            m_NetworkChannelHelper.PrepareForConnecting();
            ConnectAsync(ipAddress, port, userdata);
        }

        protected override bool ProcessSend()
        {
            if (base.ProcessSend())
            {
                SendAsync();
                return true;
            }

            return false;
        }

        private void ConnectAsync(IPAddress ipAddress,int port,object userdata)
        {
            try
            {
                m_Socket.BeginConnect(ipAddress, port, m_ConnectCallback, new ConnectState(m_Socket, userdata));
            }
            
            catch (Exception exception)
            {
                if (NetworkChannelError != null)
                {
                    SocketException socketException = exception as SocketException;
                    NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
                    return;
                }

                throw;
            }
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            ConnectState socketUserData = (ConnectState)ar.AsyncState;

            try
            {
                socketUserData.Socket.EndConnect(ar);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (Exception exception)
            {
                m_Active = false;
                if (NetworkChannelError != null)
                {
                    SocketException socketException = exception as SocketException;
                    NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
                    return;
                }

                throw;
            }
            
            m_SentPacketCount = 0;
            m_ReceivedPacketCount = 0;

            lock (m_SendPacketPool)
            {
                m_SendPacketPool.Clear();
            }

            m_ReceivePacketPool.Clear();

            lock (m_HeartBeatState)
            {
                m_HeartBeatState.Reset(true);
            }

            if (NetworkChannelConnected != null)
            {
                NetworkChannelConnected(this, socketUserData.userdata);
            }

            m_Active = true;
            ReceiveAsync();
        }

        private void SendAsync()
        {
            try
            {
                m_Socket.BeginSend(m_SendState.Stream.GetBuffer(), (int)m_SendState.Stream.Position,(int)(m_SendState.Stream.Length - m_SendState.Stream.Position),SocketFlags.None,m_SendCallback,m_Socket);
            }
            catch (Exception exception)
            {
                m_Active = false;
                if (NetworkChannelError != null)
                {
                    SocketException socketException = exception as SocketException;
                    NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
                    return;
                }

                throw;
            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            if (!socket.Connected)
            {
                return;
            }

            int byteSent = 0;
            try
            {
                byteSent = socket.EndSend(ar);
            }
            catch (Exception exception)
            {
                m_Active = false;
                if (NetworkChannelError != null)
                {
                    SocketException socketException = exception as SocketException;
                    NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
                    return;
                }

                throw;
            }
            
            m_SendState.Stream.Position += byteSent;
            if (m_SendState.Stream.Position < m_SendState.Stream.Length)
            {
                SendAsync();
                return;
            }
            
            m_SentPacketCount++;
            m_SendState.Reset();
        }
        
        private void ReceiveAsync()
        {
            try
            {
                //异步等待接收数据，接收到数据则调用回调函数，否则便一直等待
                m_Socket.BeginReceive(m_ReceiveState.Stream.GetBuffer(), (int)m_ReceiveState.Stream.Position, (int)(m_ReceiveState.Stream.Length - m_ReceiveState.Stream.Position), SocketFlags.None, m_ReceiveCallback, m_Socket);
            }
            catch (Exception exception)
            {
                m_Active = false;
                if (NetworkChannelError != null)
                {
                    SocketException socketException = exception as SocketException;
                    NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
                    return;
                }

                throw;
            }
        }
        
        private void ReceiveCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            if (!socket.Connected)
            {
                return;
            }

            int bytesReceived = 0;
            try
            {
                bytesReceived = socket.EndReceive(ar);
            }
            catch (Exception exception)
            {
                m_Active = false;
                if (NetworkChannelError != null)
                {
                    SocketException socketException = exception as SocketException;
                    NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
                    return;
                }

                throw;
            }

            if (bytesReceived <= 0)
            {
                Close();
                return;
            }

            m_ReceiveState.Stream.Position += bytesReceived;
            if (m_ReceiveState.Stream.Position < m_ReceiveState.Stream.Length)
            {
                ReceiveAsync();
                return;
            }

            m_ReceiveState.Stream.Position = 0L;

            bool processSuccess = false;
            if (m_ReceiveState.PacketHeader != null)
            {
                processSuccess = ProcessPacket();
                m_ReceivedPacketCount++;
            }
            else
            {
                processSuccess = ProcessPacketHeader();
            }
            //接收完一串完整数据后，继续等待下一串数据
            if (processSuccess)
            {
                ReceiveAsync();
                return;
            }
        }
        
        
    }
}

